%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Module summary
%%
%% Master module for a single node. 
%% Provides an interface of functions for various operations,
%% and sends commands to children. 
%% Children can be for example storage or networking. 
%%
%% Progress to usefulness:
%%
%% External function calls:
%% 
%%%%%%%%%%%%%%%%%%%%%%%

-module(node).
-behaviour(supervisor).
-include("db.hrl").
% OTP interface
-export([start_link/0]).
-export([init/1]).

% Client interface
-export([start/0,start_join/1,stop/0,
         %join/1,leave/0,
         put/2,get/1,
         status/0,status_full/0,status_nodes/0]).

% start and stop functions
start() -> 
    application:start(db),
    start_ring().

start_join(Args) ->
    case is_atom(Args) of
        true  ->  Node = Args;
        false ->  Node = lists:nth(1,Args)
    end,
    application:start(db),
    delay(500),
    join(Node).

stop() ->
    leave(),
    application:stop(db).

%
% Join and leave functions
% 
join(Node) ->
    gen_server:call(?STORAGE, {join, Node}).

start_ring() ->
    gen_server:call(?STORAGE, {start_ring}).

leave() -> 
    gen_server:call(?STORAGE, {leave}).
%
% Put & Get
%
put(Key, Value) ->
    gen_server:call(?STORAGE, {put, Key, Value}).

get(Key) ->
    gen_server:call(?STORAGE, {get, Key}).

% Get node status
status() ->
    {NetworkFmt, NetworkParams} = gen_server:call(?NETWORK, {status}),
    {BackendFmt, BackendParams} = gen_server:call(?BACKEND, {status}),
    io:format("=== Network status report: ~n"),
    io:format(NetworkFmt, NetworkParams),
    io:format("=== Backend status report: ~n"),
    io:format(BackendFmt, BackendParams),
    {ok, status}.

status_remote_node(Node) ->
    {NetworkFmt, NetworkParams} = gen_server:call({?NETWORK, Node}, {status}),
    {BackendFmt, BackendParams} = gen_server:call({?BACKEND, Node}, {status}),
    io:format("##################~n"),
    io:format("Node: ~p~n", [Node]),
    io:format("##################~n"),
    io:format("=== Network status report: ~n"),
    io:format(NetworkFmt, NetworkParams),
    io:format("=== Backend status report: ~n"),
    io:format(BackendFmt, BackendParams),
    {ok, status_remote_node}.

status_full() ->
    {NetworkFmt, NetworkParams} = gen_server:call(?NETWORK, {status_full}),
    {BackendFmt, BackendParams} = gen_server:call(?BACKEND, {status_full}),
    NetworkNodes = status_nodes(),
    io:format("=== Network status report: ~n"),
    io:format(NetworkFmt, NetworkParams),
    io:format("=== Backend status report: ~n"),
    io:format(BackendFmt, BackendParams),
    io:format("=== Status report for nodes: ~n"),
    [status_remote_node(Node) || Node <- NetworkNodes],
    {ok,status_full}.

status_nodes() ->
    gen_server:call(?NETWORK,{status_nodes}).

% OTP
start_link() ->
    io:format("Starting Node\n"),
    supervisor:start_link({local, ?NODE}, ?MODULE, []).

init(_Args) ->
    %% Start visualization process, 
    %% if environment variable is set
    visual:start(),
    ChildList = getChildList(),
    {ok, {{one_for_one, 0, 60},
          ChildList}}.

getChildList() ->
    % Check environment variables for db settings
    {_,Backend}   = application:get_env(db,backend),
    {_,Verbose}   = application:get_env(db,verbose),
    {_,NoOfSuccs} = application:get_env(db,redundancy),
    {_,Network}   = application:get_env(db,network),
    % Specify childrens
    [{store_sup, {store_sup, start_link, []},
            permanent, 5000, supervisor, [store_sup]},
    {store, {store, start_link, []},
            permanent, 5000, worker, [store]},
    {sockserv_sup, {sockserv_sup, start_link, []},
            permanent, 5000, supervisor, [sockserv_sup]}
    ] ++
    case Network of
        mesh ->
            [{mesh_sup, {mesh_sup, start_link, 
                [#mesh{verbose=Verbose,numberOfSuccs = NoOfSuccs}]}, %state
                permanent, 5000, supervisor, [mesh_sup]}];
        chord ->
            [{chord_sup, {chord_sup, start_link, []}, % starting state
                permanent, 5000, supervisor, [chord_sup]}]
    end
    ++ %% Building up child list
    case Backend of
        ets  ->
            [{ets_sup, {ets_sup, start_link,
            [#ets{verbose=Verbose}]}, % starting state
            permanent, 5000, supervisor, [ets_sup]}];
        dict ->
            [{dict_backend, {dict_backend, start_link, 
            [#dict{verbose=Verbose}]}, % starting state
            permanent, 5000, worker, [dict_backend]}]
    end.

delay(Time) ->
    receive
    after Time -> ok
    end.

